#!/usr/bin/env python
# encoding: utf-8
"""
Shovelhead
File: Engine.py
Author: Myriam Boucher

Sound synthesis of a engine.
Manages the sound of the engine and the motor changes: kickstart, shift gears,
volume, pan.
"""

from pyo import *
import random
import math
from LoopAdsr import *
from Gear import *
import time

class Engine:
    
    def __init__(self):
        
        self.env = LoopAdsr(list=[(0,0),(250,1),(1000,0.0005),(7900,0.0005),(8192,0)],
                dur=0.9, min=0.17, max=0.20, freq=[8.45,9.2])
        self.env2 = LoopAdsr(list=[(0,0),(250,1),(700,0.0001),(8000,0.0001),(8192,0)],
                dur=0.057, min=0.12, max=0.16, freq=[8.50,9.32])
        self.env3 = LoopAdsr(list=[(0,0),(500,1),(1500,0.8),(6000,0.8),(8192,0)],
                dur=1.55, min=1.7, max=1.8, freq=[5.2,7.33])
     
        # variation of the roar of the engine
        self.pinkNoiseMoteur = PinkNoise(mul=self.env.sig(), add=0)
        self.eqPinkNoise = Biquad(self.pinkNoiseMoteur, freq=200, q=10, type=0, mul=1, add=0)

        # randoms to vary the parameters of the roar of the engine
        self.r1 = Randi(min=0.65, max=.72, freq=1.00, mul=1, add=0)
        self.r1_2 = Randi(min=0.5, max=.95, freq=1.00, mul=1, add=0)
        self.r2 = Randi(min=0.45, max=.5, freq=1.00, mul=1, add=0)
        self.r3 = Randi(min=0.3, max=.35, freq=1.00, mul=1, add=0)
        self.r4 = Randi(min=0.12, max=.17, freq=1.00, mul=1, add=0)
        self.r4_2 = Randi(min=0.3, max=.85, freq=1.00, mul=1, add=0)
        
        # grinding and slamming engine
        self.fm = CrossFM(carrier=self.r1*100, ratio=self.r2, ind1=self.r3*10, ind2=self.r4*10, mul=self.env.sig(), add=0)
        self.fm2 = CrossFM(carrier=self.r1_2*100, ratio=self.r2+.1, ind1=self.r3*10, ind2=self.r4_2*10, mul=self.env2.sig(), add=0) 
        self.sinwave = SineLoop(freq=self.r4*150, feedback=.1, mul=self.env.sig(), add=0)
        self.filterSinwave = Biquad(self.sinwave, freq=1000, q=30, type=2, mul=1, add=0)
        self.clipFm = Clip(self.fm + self.fm2, min=-1.00, max=1.00, mul=.5, add=0)
        
        # mix - snap engine
        self.pan2D = None
        self.mix = None
        self.mixNoStarting = None
        self.gear = Gear(1, self) #engine gear
        
    def setMixNoStarting(self, mix2):
        # mix if the engine did not start
        self.mixNoStarting = mix2
        
    def getGearEngine(self):
        # @return: engine gear
        if self.gear != None:
            return self.gear
        else:
            self.gear = Gear(1, self)
            return self.gear
    
    def setPan2D(self, pan):
        self.pan2D = pan
        
    def setMix(self, mix2):
        self.mix = mix2
              
    def kickStartEngine(self):
        # crankshaft
        startOrNot = random.choice(['starting', 'notStarting'])
        #print startOrNot  # to see if the engine is started
        if startOrNot == 'notStarting':  # the engine did not start
            self.tryStartingOut()
        else:     # the engine has started
            self.tryStartingOut()
            self.gear.startingOut()

        
    def tryStartingOut(self):
        # the engine tries to start.
        self.mixNoStarting.out()
        time.sleep(1)
        self.stopSound()
        return self                
    
    def outEngine(self):
        # sends the engine sound to the speakers.
        self.playSound()
        self.mix.out()
        return self
 
    def stopSound(self):
        # stop the engine audio objects.
        self.mix.stop()
        self.mixNoStarting.stop()
        self.pan2D.stop()
        self.pinkNoiseMoteur.stop()
        self.eqPinkNoise.stop()
        self.fm.stop()
        self.fm2.stop()
        self.sinwave.stop()
        self.filterSinwave.stop()
        self.clipFm.stop()
        self.r1.stop()
        self.r1_2.stop()
        self.r2.stop()
        self.r3.stop()
        self.r4.stop()
        self.r4_2.stop()
        return self
    
    def playSound(self):
        # plays the engine audio objects.
        self.mix.play()
        self.mixNoStarting.play()
        self.pan2D.play()
        self.pinkNoiseMoteur.play()
        self.eqPinkNoise.play()
        self.fm.play()
        self.fm2.play()
        self.sinwave.play()
        self.filterSinwave.play()
        self.clipFm.play()
        self.r1.play()
        self.r1_2.play()
        self.r2.play()
        self.r3.play()
        self.r4.play()
        self.r4_2.play()
        return self
        
    def panSpatialization(self,x):
        # manage audio voice routing and spread of a sound
        # according to the position of the motocycle.
        self.pan2D.setPan(x)

    def getMixEngine(self):
        # @return: engine mix
        return self.mix